Skip to content

Fixed Stripe Service coming up if billing portal manager errors#27468

Open
sam-lord wants to merge 1 commit intomainfrom
fix-billing-portal-configuration-crash
Open

Fixed Stripe Service coming up if billing portal manager errors#27468
sam-lord wants to merge 1 commit intomainfrom
fix-billing-portal-configuration-crash

Conversation

@sam-lord
Copy link
Copy Markdown
Contributor

ref ONC-1656

Prevents the whole Stripe Service from erroring if the billing portal manager encounters an error while trying to update the billing portal configuration. This can happen if the billing portal configuration that Ghost has created has been made the default one by the Stripe Account owner.

@sam-lord sam-lord requested a review from aileen April 20, 2026 13:53
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0a0cd927-7c34-4d4a-8d78-38707eb532c3

📥 Commits

Reviewing files that changed from the base of the PR and between ce6d110 and 30720f0.

📒 Files selected for processing (2)
  • ghost/core/core/server/services/stripe/billing-portal-manager.js
  • ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js
✅ Files skipped from review due to trivial changes (1)
  • ghost/core/core/server/services/stripe/billing-portal-manager.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js

Walkthrough

Error handling in BillingPortalManager.createOrUpdateConfiguration was changed: when updateBillingPortalConfiguration rejects with an error other than resource_missing, the method no longer re-throws the error. Instead, it logs the failure and returns the existing billing portal configuration ID. The unit test was updated to expect a resolved result returning the original configuration ID ('bpc_existing') instead of a rejection.

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately reflects the main change: preventing the Stripe Service from failing when billing portal manager encounters errors.
Description check ✅ Passed The description is directly related to the changeset, explaining the context and rationale for preventing Stripe Service failures due to billing portal configuration errors.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-billing-portal-configuration-crash

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js (1)

188-189: Strengthen this regression test with call assertions.

On Line 188-Line 189, this validates only the return value. Add API interaction assertions to lock behavior (update attempted, no fallback create).

Suggested test diff
             const result = await manager.createOrUpdateConfiguration('bpc_existing');
             assert.equal(result, 'bpc_existing');
+            sinon.assert.calledOnce(mockApi.updateBillingPortalConfiguration);
+            sinon.assert.notCalled(mockApi.createBillingPortalConfiguration);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js`
around lines 188 - 189, The test only asserts the return value of
manager.createOrUpdateConfiguration('bpc_existing'); add assertions that the
billing-portal configuration update API was invoked and that the create API was
not called: e.g., assert that the stub/spy for the billing portal
configurations.update method was called once with an object containing id
'bpc_existing' (or the expected params) and assert that the
configurations.create stub/spy was not called; use the existing sinon/sandbox
stubs used in this test file to check calls on the billing portal client so the
test locks behavior to "update attempted, no fallback create."
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@ghost/core/core/server/services/stripe/billing-portal-manager.js`:
- Around line 95-96: The catch-all that returns id on any error (except
resource_missing) hides real Stripe/API failures; update the try/catch around
the billing portal config modification in billing-portal-manager.js to log the
full Stripe error (error.type, error.code, error.message, and stack) using the
module's logger, and only swallow the error when it exactly matches the
confirmed "default config" signature (compare error.code/type/message
explicitly); rethrow or surface any other unexpected errors and add a
breadcrumb/monitoring event so startup failures are alerted.

---

Nitpick comments:
In `@ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js`:
- Around line 188-189: The test only asserts the return value of
manager.createOrUpdateConfiguration('bpc_existing'); add assertions that the
billing-portal configuration update API was invoked and that the create API was
not called: e.g., assert that the stub/spy for the billing portal
configurations.update method was called once with an object containing id
'bpc_existing' (or the expected params) and assert that the
configurations.create stub/spy was not called; use the existing sinon/sandbox
stubs used in this test file to check calls on the billing portal client so the
test locks behavior to "update attempted, no fallback create."
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1af96786-0891-4b45-a46c-35c77adce480

📥 Commits

Reviewing files that changed from the base of the PR and between f6edd55 and d57953b.

📒 Files selected for processing (2)
  • ghost/core/core/server/services/stripe/billing-portal-manager.js
  • ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js

Comment thread ghost/core/core/server/services/stripe/billing-portal-manager.js
@sam-lord sam-lord force-pushed the fix-billing-portal-configuration-crash branch from d57953b to ce6d110 Compare April 20, 2026 14:41
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js (1)

176-190: Strengthen this regression test with interaction assertions

Line 188-Line 189 validates the returned ID, but this can still pass if the update path is skipped or create fallback is used accidentally. Add explicit call assertions to lock the intended branch behavior.

Proposed test hardening
         it('returns the passed id when failing with a non-resource based error', async function () {
             mockSettingsCache.get.withArgs('title').returns('Test Site');
             const genericError = new Error('Stripe API error');
             mockApi.updateBillingPortalConfiguration.rejects(genericError);

             const manager = new BillingPortalManager({
                 api: mockApi,
                 models: {Settings: mockSettingsModel},
                 settingsCache: mockSettingsCache
             });
             manager.configure({siteUrl: 'https://example.com'});

             const result = await manager.createOrUpdateConfiguration('bpc_existing');
             assert.equal(result, 'bpc_existing');
+            sinon.assert.calledOnce(mockApi.updateBillingPortalConfiguration);
+            sinon.assert.notCalled(mockApi.createBillingPortalConfiguration);
+            assert.equal(mockApi.updateBillingPortalConfiguration.firstCall.args[0], 'bpc_existing');
         });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js`
around lines 176 - 190, The test must assert the API interactions to ensure the
update path was exercised; after calling
BillingPortalManager.createOrUpdateConfiguration('bpc_existing') add assertions
that mockApi.updateBillingPortalConfiguration was called once with the existing
id (and expected payload) and that mockApi.createBillingPortalConfiguration was
not called. Locate the test using BillingPortalManager and the
createOrUpdateConfiguration invocation and add these call assertions (e.g.
sinon/assert-style checks) referencing mockApi.updateBillingPortalConfiguration
and mockApi.createBillingPortalConfiguration to lock the intended branch
behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js`:
- Around line 176-190: The test must assert the API interactions to ensure the
update path was exercised; after calling
BillingPortalManager.createOrUpdateConfiguration('bpc_existing') add assertions
that mockApi.updateBillingPortalConfiguration was called once with the existing
id (and expected payload) and that mockApi.createBillingPortalConfiguration was
not called. Locate the test using BillingPortalManager and the
createOrUpdateConfiguration invocation and add these call assertions (e.g.
sinon/assert-style checks) referencing mockApi.updateBillingPortalConfiguration
and mockApi.createBillingPortalConfiguration to lock the intended branch
behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 626c5ab4-c3a9-4389-87aa-5b2ee82fecb8

📥 Commits

Reviewing files that changed from the base of the PR and between d57953b and ce6d110.

📒 Files selected for processing (2)
  • ghost/core/core/server/services/stripe/billing-portal-manager.js
  • ghost/core/test/unit/server/services/stripe/billing-portal-manager.test.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • ghost/core/core/server/services/stripe/billing-portal-manager.js

ref ONC-1656

Prevents the whole Stripe Service from erroring if the billing portal
manager encounters an error while trying to update the billing portal
configuration. This can happen if the billing portal configuration
that Ghost has created has been made the default one by the Stripe
Account owner.
@sam-lord sam-lord force-pushed the fix-billing-portal-configuration-crash branch from 6382e68 to 30720f0 Compare April 20, 2026 14:55
@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown
Contributor

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 24673507120 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

2 similar comments
@github-actions
Copy link
Copy Markdown
Contributor

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 24673507120 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

@github-actions
Copy link
Copy Markdown
Contributor

E2E Tests Failed

To view the Playwright test report locally, run:

REPORT_DIR=$(mktemp -d) && gh run download 24673507120 -n playwright-report -D "$REPORT_DIR" && npx playwright show-report "$REPORT_DIR"

}
throw err;

logging.error('Failed to update the billing portal configuration', {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick: if we wanted to monitor those logs, it might be useful to have some further information in them, so we could action on it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the error itself should help! That will have a message w plenty of context

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants